package fr.emse.tatiana.analysis.annotation.categorisation;

import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColorCellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableColorProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import java.awt.Color;

import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;

import fr.emse.tatiana.TatianaConstants;
import fr.emse.tatiana.analysis.annotation.AnnotationAnalysis;
import fr.emse.tatiana.analysis.annotation.AnnotationAnalysis.AnalysisContentProvider;
import fr.emse.tatiana.analysis.annotation.AnnotationAnalysis.AnalysisLabelProvider;
import fr.emse.tatiana.display.BadDisplayFile;
import fr.emse.tatiana.display.DisplayFileReader;
import fr.emse.tatiana.display.DisplayFileWriter;
import fr.emse.tatiana.display.Ut;
import fr.emse.tatiana.replayable.Anchor;
import fr.emse.tatiana.replayable.analysis.AbstractAnalysis;
import fr.emse.tatiana.replayable.analysis.AnalysisEvent;
import fr.emse.tatiana.replayable.analysis.AnalysisEventListener;
import fr.emse.tatiana.replayable.analysis.IAnalysis;
import fr.emse.tatiana.utils.AutocompleteComboBoxCellEditor;

public class CategorisationAnalysis extends AnnotationAnalysis {

	public final static String DISPLAY_TYPE = "fr.emse.tatiana.categorisationanalysis"; 
	public final static String CATEG_DISPLAY_TYPE = "fr.emse.tatiana.categorisation";
	protected Integer nullint = new Integer(0);
	protected Vector<CatStruct> categs;
	protected IFile categfile;
	protected ComboBoxCellEditor chooser;
	private boolean savedcateg;

	public CategorisationAnalysis(String string, IProject p, IFile categfile) throws BadDisplayFile {
		super(string,p);
		this.categfile=categfile;
		if (categfile==null) {
			categs = new Vector<CatStruct>();
			categs.add(new CatStruct());
			savedcateg=true;
		} else {
			this.parseCategFile(new DisplayFileReader(categfile.getLocation().toString()));
			savedcateg=true;
		}
	}

	public CategorisationAnalysis(IFile file) throws BadDisplayFile {
		super(file);
	}
	
	@Override
	protected void doInit() {
		// do nothing ??? (a bit surprising)
		
	}

	protected void parseFile(DisplayFileReader reader) throws BadDisplayFile {
		if (!DISPLAY_TYPE.equals(reader.getType())) {
			throw new BadDisplayFile(BadDisplayFile.WRONGFILETYPE +" expected:"+DISPLAY_TYPE);
		}
		reader.selectFirstItem();
		String catfilepath = reader.getStringValue("category-file", BadDisplayFile.MISSINGFIELD);
		categfile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(catfilepath));
		this.parseCategFile(new DisplayFileReader(categfile.getLocation().toString()));
		for (boolean notdone = reader.selectNextItem();notdone;notdone = reader.selectNextItem()) {
			Anchor anchor = reader.getAnchorValue("target-anchor", BadDisplayFile.MISSINGFIELD);
			Anchor catanchor = reader.getAnchorValue("categ-anchor", BadDisplayFile.MISSINGFIELD);
			Integer catID = nullint;
			for (int i=1; i<categs.size(); i++) {
				if (categs.get(i).anchor.equals(catanchor)) {
					catID= new Integer(i);
				}
			}
			if (catID == nullint) {
				System.err.println("category in analysis with no match in categories file" + catanchor.toString());
			} else {
				values.put(anchor,catID);
			}
		}
		saved = true;
		savedcateg = true;
		fireAnalysisEvent(new AnalysisEvent(this));
	}

	private void parseCategFile(DisplayFileReader reader) throws BadDisplayFile {
		categs = new Vector<CatStruct>();
		categs.add(new CatStruct());
		if (!CATEG_DISPLAY_TYPE.equals(reader.getType())) {
			throw new BadDisplayFile(BadDisplayFile.WRONGFILETYPE +" expected:"+CATEG_DISPLAY_TYPE);
		}
		for (boolean notdone = reader.selectFirstItem();notdone;notdone = reader.selectNextItem()) {
			CatStruct cat = new CatStruct();
			cat.anchor = reader.getAnchorValue("src-anchor", BadDisplayFile.MISSINGFIELD);
			cat.description = reader.getStringValue("description", BadDisplayFile.MISSINGFIELD);
			cat.name = reader.getStringValue("name", BadDisplayFile.MISSINGFIELD);
			try {
				reader.selectFirstInfo("rainbow-color");
				reader.updateInfoValue();
				cat.color = (java.awt.Color) reader.infoValue();
			} catch (ClassCastException cce) {
				throw new BadDisplayFile(BadDisplayFile.MISSINGFIELD);
			}
			categs.add(cat);
		}
	}

	public boolean saveCategFile(IFile file) {
		categfile=file;
		
		DisplayFileWriter writer = new DisplayFileWriter(file.getLocation().toString(),CATEG_DISPLAY_TYPE);
		String path = categfile.getFullPath().toString();
		for (int i=1; i<categs.size(); i++) {
			CatStruct cat = categs.get(i);
			if (cat.description == null) cat.description="";
			cat.anchor= new Anchor(path,i);
			writer.newItem();
			writer.newInfoFromString("src-anchor",cat.anchor.toString());
			writer.newInfo("name", "", cat.name);
			writer.newInfo("description", "", cat.description);
			writer.newInfo("rainbow-color", "", cat.color);
		}
		writer.close();
		savedcateg = true;
		try {
			file.refreshLocal(IResource.DEPTH_ZERO, null);
		} catch (CoreException e) {
			return false;
		}
		return true;
	}

	protected org.eclipse.swt.graphics.Color doGetBackGround(Object idx) {
		CatStruct cat = categs.get((Integer) idx);
		return new org.eclipse.swt.graphics.Color(Display.getCurrent(),Ut.toSWTRGB(cat.color));
	}

	protected Object doGetNullValue() {
		return nullint;
	}

	protected String doGetStringValue(Object ret) {
		return categs.get((Integer) ret).name;
	}

	public CellEditor getChooser(Composite parent) {
		AutocompleteComboBoxCellEditor chooser = new AutocompleteComboBoxCellEditor(parent, new String[]{""}, SWT.READ_ONLY);;
			       
		updateCellChooserItems(chooser);
	    
		return chooser;
	}

	public void updateCellChooserItems(AutocompleteComboBoxCellEditor chooser) {
		if (chooser == null)
			return;
		Vector<String> names = new Vector<String>();
		for (CatStruct cat : categs) {
			names.add(cat.name);
		}
		chooser.setItems(names.toArray(new String[]{}));
	}

	public String getCategorisationFileName() {
		if (categfile==null || categfile.getName().startsWith(".")) return "";
		return categfile.getName();
	}

	protected boolean saveToAnalysisFile(IFile file) {
		if (!savedcateg) {
			if (categfile==null) {
				IFolder parent = (IFolder) file.getParent();
				categfile = parent.getFile("." + file.getName() + "-categ.xml");
			}
			if (!saveCategFile(categfile)) return false;
		}
		DisplayFileWriter writer = new DisplayFileWriter(file.getLocation().toString(),DISPLAY_TYPE);
		writer.newItem();
		writer.newInfo("category-file", "", categfile.getFullPath().toString());
		for (Anchor anchor : values.keySet()) {
			CatStruct cat = categs.get((Integer)values.get(anchor));
			if (cat.anchor != null) {
				writer.newItem();
				writer.newInfoFromString("target-anchor", anchor.toString());
				writer.newInfoFromString("categ-anchor", cat.anchor.toString());
			}
		}
		writer.close();
		saved = true;
		return true;
	}

	private class CatStruct {
		public Anchor anchor=null; // the anchor is what is important: null category has null anchor
		public String name="";
		public String description="";
		public Color color=java.awt.Color.WHITE;

		public CatStruct() {
			super();
			name = "        ";
			description="";
			color=java.awt.Color.WHITE;
		}
	}

	public IStructuredContentProvider getContentProvider() {
		return new CategorisationAnalysisContentProvider();
	}

	public IBaseLabelProvider getLabelProvider() {
		return new CategorisationAnalysisLabelProvider();
	}


	private class CategorisationAnalysisContentProvider extends AnnotationAnalysis.AnalysisContentProvider {

		public Object[] getElements(Object inputElement) {
			if (categs==null) {
				return new Object[]{};
			}
			Vector<CatStruct> copy = new Vector<CatStruct>();
			for (int i=1; i<categs.size(); i++) {
				copy.add(categs.get(i));
			}
			return copy.toArray();
		}

	}

	private class CategorisationAnalysisLabelProvider extends  LabelProvider implements ITableLabelProvider, ITableColorProvider {

		public Image getColumnImage(Object element, int columnIndex) {
			return null;
		}

		public String getColumnText(Object element, int columnIndex) {
			if (!(element instanceof CatStruct)) {
				return null;
			}
			CatStruct cat = (CatStruct) element;
			switch(columnIndex) {
			case 0: return cat.name;
			case 1: return cat.description;
			case 2: return "";
			case 3: return cat.name;
			default: return null;
			}
		}

		public org.eclipse.swt.graphics.Color getBackground(Object element,
				int columnIndex) {
			if (!(element instanceof CatStruct)) {
				return null;
			}
			CatStruct cat = (CatStruct) element;
			switch(columnIndex) {
			case 2: // is the following colour ever disposed?
			case 3: return new org.eclipse.swt.graphics.Color(Display.getCurrent(),Ut.toSWTRGB(cat.color));
			case 0: 
			case 1: 
			default: return null;
			}
		}

		public org.eclipse.swt.graphics.Color getForeground(Object element,
				int columnIndex) {
			return null;
		}

	}

	public boolean canModify(Object element, String property) {
		return !property.equals(RESULT);
	}

	public Object getValue(Object element, String property) {
		if (!(element instanceof CatStruct)) {
			return null;
		}
		CatStruct cat = (CatStruct) element;
		if (property.equals(NAME)) {
			return cat.name;
		} else if (property.equals(DESC)) {
			return cat.description;
		} else if (property.equals(COLOR)) {
			return Ut.toSWTRGB(cat.color);
		}
		return null;
	}

	public void modify(Object element, String property, Object value) {
		if (element instanceof Item) {
			element = ((Item) element).getData();
		}
		if (!(element instanceof CatStruct)) {
			return;
		}
		boolean diff = true;
		CatStruct cat = (CatStruct) element;
		if (property.equals(NAME)) {
			if (cat.name.equals((String) value)) {
				diff=false;
			} else {
				cat.name = (String) value;
			}
		} else if (property.equals(DESC)) {
			if (cat.description.equals((String) value)) {
				diff=false;
			} else {
				cat.description = (String) value;
			}
		} else if (property.equals(COLOR)) {
			Color c = Ut.toAWTColor((RGB) value);
			if (cat.color.equals(c)) {
				diff=false;
			} else {
				cat.color = c;
			}
		}
		if (diff) {
			Vector<Anchor> chgd = new Vector<Anchor>();
			Integer v = categs.indexOf(cat);
			for (Anchor a : values.keySet()) {
				if (v.equals(values.get(a))) {
					chgd.add(a);
				}
			}
			savedcateg=false;
			fireAnalysisEvent(new AnalysisEvent(this, chgd));
		}
	}

	public String[] getColumnNames() {
		return new String[]{NAME,DESC,COLOR,RESULT};
	}

	public CellEditor[] getCellEditors(Composite parent) {
		return new CellEditor[]{
				new TextCellEditor(parent),
				new TextCellEditor(parent),
				new ColorCellEditor(parent),
				null
		};
	}

	public int getWidth(int col) {
		switch(col) {
		case 1:
			return 300;
		case 2:
			return 100;
		case 0:
		case 3:
			return 150;
		default:
			return 0;
		}
	}

	private final static String NAME = "Name";
	private final static String DESC = "Description";
	private final static String COLOR = "Colour";
	private final static String RESULT = "Appearance";

	public String getEditorID() {
		return CategorisationAnalysisEditor.ID;
	}

	public void addCategory(String value) {
		CatStruct cat = new CatStruct();
		cat.name=value;
		categs.add(cat);
		savedcateg=false;
		//updateCellChooserItems();
		fireAnalysisEvent(new AnalysisEvent(this,AnalysisEvent.ELEMENT_ADD, cat));
	}

	public void removeCategory(IStructuredSelection sel) {
		CatStruct cat = (CatStruct) sel.getFirstElement();
		Integer val = categs.indexOf(cat);
		categs.remove(cat);
		Vector<Anchor> chgd = new Vector<Anchor>();
		Vector<Anchor> toremove = new Vector<Anchor>();
		for (Anchor a : values.keySet()) {
			Integer toChange = (Integer) values.get(a);
			if (val.equals(values.get(a))) {
				toremove.add(a);
				chgd.add(a);
			} else if (toChange.compareTo(val)>0) {
				setValue(a,toChange-1);
				chgd.add(a);
			}
		}
		for (Anchor a : toremove) {
			values.remove(a);
		}
		savedcateg=false;
		//updateCellChooserItems();
		fireAnalysisEvent(new AnalysisEvent(this, chgd));
	}

	public boolean isSaved() {
		return super.isSaved() && savedcateg;
	}

	public void saveCategFileAs(String value) {
		IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(TatianaConstants.TATIANA_PROJECT_NAME);
		IFile file = project.getFolder(TatianaConstants.CATEGORIES_FOLDER_NAME).getFile(value);
		this.saveCategFile(file);
		fireAnalysisEvent(new AnalysisEvent(this));
	}



}
